package psn.kiko.interceptor;

import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import psn.kiko.constant.DBConstant;
import psn.kiko.constant.MessageConstant;
import psn.kiko.context.UserContext;
import psn.kiko.excepion.ApiAuthorityException;
import psn.kiko.excepion.UserOperationException;
import psn.kiko.pojo.entity.system.InterfaceEntity;
import psn.kiko.pojo.entity.system.InterfaceRoleEntity;
import psn.kiko.pojo.entity.system.RoleEntity;
import psn.kiko.pojo.entity.system.UserEntity;
import psn.kiko.pojo.entity.system.UserRoleEntity;
import psn.kiko.pojo.vo.AllocateRoleVo;
import psn.kiko.service.business.associated.InterfaceRoleService;
import psn.kiko.service.business.associated.UserRoleService;
import psn.kiko.service.business.system.InterfaceService;
import psn.kiko.service.business.system.RoleService;
import psn.kiko.service.business.system.UserService;
import psn.kiko.util.encrypt.Tokens;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 管理端--处理器拦截器
 */
@Component
@Slf4j
public class AdminLoginHandlerInterceptor implements HandlerInterceptor{
	@Autowired
	Tokens tokens;
	@Autowired
	InterfaceService interfaceService;
	@Autowired
	UserRoleService userRoleService;
	@Autowired
	InterfaceRoleService interfaceRoleService;
	@Autowired
	UserService userService;
	@Autowired
	RoleService roleService;
	
	/**
	 *<p>进行token、api访问验证</p>
	 * <p style="color:red">token验证未通过抛异常、api验证未通过也抛异常</p>
	 */
	@Override
	public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception{
		// 若为非处理器方法，放行
		if(!( handler instanceof HandlerMethod )){
			return true;
		}
		
		// 1、开始token验证
		String userToken = request.getHeader(tokens.getTokenProperties().getTokenName());
		boolean tokenValidationPassed = tokens.validate(userToken);
		log.debug("admin端token验证：{}-{}: validated ? {}",request.getMethod(),request.getRequestURI(),tokenValidationPassed);
		
		//1、token验证未通过，抛出异常
		if(!tokenValidationPassed){
			throw new UserOperationException(MessageConstant.USER_LOGIN_EXPIRED);
		}else{
			// 若token验证通过，继续api访问验证
			Claims claims = tokens.getClaimsFrom(userToken);
			String userId = claims.getSubject();
			// 1.1、将用户id存入ThreadLocal
			UserContext.setUserId(Integer.valueOf(userId));
			
			// 1.2、开始api访问验证
			boolean apiValidationPassed = validateApiAuthority((HandlerMethod) handler);
			// 若api访问验证也通过，放行
			return apiValidationPassed;
		}
	}
	
	
	/**
	 * api访问权限验证
	 *
	 * @return <p style="color:green">true-api访问验证通过</p>
	 * <p style="color:red">其它情况抛出api验证异常，由异常处理器返回异常响应</p>
	 */
	private boolean validateApiAuthority(HandlerMethod handler){
		//1、查询用户
		UserEntity userEntity = userService.getUserById(UserContext.getUserId());
		//		若用户不存在
		if(userEntity == null){
			throw new ApiAuthorityException(MessageConstant.USER_NOT_EXIST);
		}
		//		若用户不可编辑（即超级管理员）
		if(userEntity.getEditable() == DBConstant.UN_EDITABLE){
			log.info("{} 用户无需鉴权",userEntity.getName());
			return true;
		}
		//		若用户被禁用
		if(userEntity.getEnabled() == DBConstant.DISABLE)
			throw new ApiAuthorityException(MessageConstant.USER_WAS_DISABLED);
		//正在被请求的接口路径、方式
		List<String> list = requestPathAndMethod(handler);
		String fullRequestPath = list.get(0);//完整请求路径
		String requestMethod = list.get(1);//请求方式
		//2、查询接口表
		InterfaceEntity interfaceEntity = interfaceService.selectByPathAndMethod(fullRequestPath,requestMethod);
		//		若接口不存在（指接口未解析入库）
		if(interfaceEntity == null){
			throw new ApiAuthorityException(fullRequestPath+MessageConstant.INTERFACE_NOT_EXIST);
		}
		//		若接口存在，但被禁用
		if(interfaceEntity.getEnabled() == DBConstant.DISABLE){
			throw new ApiAuthorityException(fullRequestPath+MessageConstant.INTERFACE_ITSELF_WAS_DISABLED);
		}
		
		//3、查询用户-角色表(即获取当前用户已分配的角色)
		List<AllocateRoleVo> allocatedRolesOfCurrentUser = userRoleService.selectAllocatedRolesByOfUserId(userEntity.getId());
		//		若用户未分配任何角色
		if(allocatedRolesOfCurrentUser.isEmpty()){
			throw new ApiAuthorityException(MessageConstant.USER_WAS_ALLOCATED_NONE_ROLES);
		}
		//得到当前用户的所有角色id
		List<Integer> roleIds = allocatedRolesOfCurrentUser.stream()
				.map(AllocateRoleVo::getId)
				.collect(Collectors.toList());
		
		//4、查询接口-角色表
		InterfaceRoleEntity interfaceRoleEntity = interfaceRoleService.getByApiIdAndRoleIds(interfaceEntity.getStableId(),
				roleIds);
		//		若用户未分配当前接口（即当前用户已分配的角色中，没有一个可以访问当前接口）
		if(interfaceRoleEntity == null){
			throw new ApiAuthorityException(fullRequestPath+MessageConstant.INTERFACE_WAS_NOT_ALLOCATED_TO_USER);
		}
		//		若接口-角色 处于禁用状态（即当前用户已分配的角色中，至少有一个角色可以访问当前接口，但是被禁用）
		if(interfaceRoleEntity.getEnabled() == DBConstant.DISABLE)
			throw new ApiAuthorityException(fullRequestPath+MessageConstant.INTERFACE_WAS_ALLOCATED_BUT_DISABLED);
		
		//5、查询角色表
		RoleEntity roleEntity = roleService.getRoleById(interfaceRoleEntity.getRoleId());
		//		若角色被禁用
		if(roleEntity.getEnabled() == DBConstant.DISABLE){
			throw new ApiAuthorityException(roleEntity.getName()+MessageConstant.ROLE_ITSELF_WAS_DISABLED);
		}
		
		//6、查询用户-角色表
		UserRoleEntity userRoleEntity = userRoleService.getByUserIdAndRoleId(userEntity.getId(),interfaceRoleEntity.getRoleId());
		//		若用户-角色 处于禁用状态（即当前用户已分配了能够访问此接口的角色，但被禁用）
		if(userRoleEntity.getEnabled() == DBConstant.DISABLE){
			throw new ApiAuthorityException(roleEntity.getName()+MessageConstant.ROLE_WAS_ALLOCATED_TO_USER_BUT_DISABLED);
		}
		
		//权限校验通过
		return true;
	}
	
	/**
	 * <p style="color:green;">解析此次请求的完整请求路径，以及请求方式</p>
	 *
	 * @return 两个元素的集合，第一个为请求完整路径，形如 /xx/xx/zz ;第二个为请求方式
	 */
	private List<String> requestPathAndMethod(HandlerMethod handler){
		Object bean = handler.getBean();
		Class<?> beanClass = bean.getClass();
		RequestMapping classAnnotation = beanClass.getDeclaredAnnotation(RequestMapping.class);
		String[] prefixPaths = classAnnotation.value();
		RequestMapping handlerAnnotation = handler.getMethodAnnotation(RequestMapping.class);
		String[] suffixPaths = handlerAnnotation.value();
		RequestMethod[] requestMethods = handlerAnnotation.method();
		//请求路径类部分
		String prefixPath = "";
		if(prefixPaths.length > 0){
			prefixPath = prefixPaths[0];
			//添加请求前缀中的首尾斜线
			prefixPath = prefixPath.startsWith("/") ? prefixPath : "/"+prefixPath;
			prefixPath = prefixPath.endsWith("/") ? prefixPath : prefixPath+"/";
		}
		//请求路径方法部分
		String suffixPath = "";
		if(suffixPaths.length > 0){
			suffixPath = suffixPaths[0];
			//去除请求后缀中的首尾斜线
			suffixPath = suffixPath.startsWith("/") ? suffixPath.replaceFirst("/","") : suffixPath;
			suffixPath = suffixPath.endsWith("/") ? suffixPath.substring(0,suffixPath.lastIndexOf("/")) : suffixPath;
		}
		// 形如/xx/xx/xx/zz 的完整请求路径
		String fullRequestPath = prefixPath+suffixPath;
		List<String> list = new ArrayList<>(2);
		list.add(fullRequestPath);
		list.add(requestMethods[0].name());
		return list;
	}
}
